﻿using System.Linq;
using CID.Domain.Exceptions;
using CID.Domain.Infrastructure;
using CID.Tests.Support;
using NUnit.Framework;
using Rhino.Mocks;

namespace CID.Domain.Tests.Infrastructure
{
    [TestFixture]
    public class DeploymentDefinitionTests : BaseFunctionalTests
    {
        private DeploymentDefinition definition;
        private IParameter[] deploymentParameters;

        public override void SetUp()
        {
            base.SetUp();
            var deploymentTemplate= StubsBuilder.BuildDeploymentTemplateStub();

            var parameter1 = StubsBuilder.BuildParameterStub(template: deploymentTemplate);
            var parameter2 = StubsBuilder.BuildParameterStub(template: deploymentTemplate);
            var parameter3 = StubsBuilder.BuildParameterStub(template: deploymentTemplate);
            deploymentParameters = new IParameter[] { parameter1, parameter2, parameter3 };
            deploymentTemplate.Stub(x => x.Parameters).Return(deploymentParameters);

            definition = new DeploymentDefinition(Development.StubsBuilder.BuildVersionStub());
            definition.DeploymentTemplate = deploymentTemplate;
        }

        #region Constructor
        [Test]
        public void CreateDeploymentDefinition_FreshVersion_ShouldSucceed()
        {
            var version = Development.StubsBuilder.BuildVersionStub();

            var definition = new DeploymentDefinition(version);

            Assert.AreEqual(version, definition.Version);
        }

        [Test, ExpectedException(typeof(ParentAlreadyAssignedException))]
        public void ActivationDeactivationDefinition_ComponentWithActivationDeactivationDefinitionAssigned_ShouldThrowException()
        {
            var version = Development.StubsBuilder.BuildVersionStub();
            var anotherDefinition = new DeploymentDefinition(version);
            version.Stub(x => x.DeploymentDefinition).Return(anotherDefinition);

            var definition = new DeploymentDefinition(version);
        }
        #endregion

        #region AddEnvironmentParameterValue
        [Test, ExpectedException(typeof(InvalidParameterValueDefinitionException))]
        public void AddEnvironmentParameterValue_ParameterValueWithParameterAssignedToAnotherTemplate_ShouldThrowException()
        {
            var template = StubsBuilder.BuildTemplateStub();
            var parameter = StubsBuilder.BuildParameterStub(template: template);
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();

            definition.AddEnvironmentParameterValue(parameter, environment);
        }

        [Test]
        public void AddEnvironmentParameterValue_ParameterValueWithParameterAssignedToDeploymentTemplate_ShouldSucced()
        {
            var defaultParameterValuesCount = definition.DefaultParameterValues.Count();
            var parameter = StubsBuilder.BuildParameterStub(template: definition.DeploymentTemplate);
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();

            var parameterValue = definition.AddEnvironmentParameterValue(parameter, environment);

            Assert.AreEqual(definition, parameterValue.Definition);
            Assert.AreEqual(parameter, parameterValue.Parameter);
            Assert.AreEqual(environment, parameterValue.Environment);
            Assert.AreEqual(defaultParameterValuesCount + 1, definition.ParameterValues.Count());
            Assert.AreEqual(1, definition.EnvironmentParameterValues.Count());
            CollectionAssert.Contains(definition.ParameterValues, parameterValue);
            CollectionAssert.Contains(definition.EnvironmentParameterValues, parameterValue);
        }
        #endregion

        #region GetDeploymentParameterValues
        [Test]
        public void GetDeploymentParameterValues_EnvironmentParameterValuesAdded_ShouldReturnAllEnvironmentParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.AddEnvironmentParameterValue(deploymentParameters[0], environment);
            var parameterValue2 = definition.AddEnvironmentParameterValue(deploymentParameters[1], environment);
            var parameterValue3 = definition.AddEnvironmentParameterValue(deploymentParameters[2], environment);

            var parameterValues = definition.GetDeploymentParamaterValues(environment);

            Assert.AreEqual(deploymentParameters.Count(), parameterValues.Count());
            CollectionAssert.AllItemsAreInstancesOfType(parameterValues, typeof(IEnvironmentParameterValue));
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }

        [Test]
        public void GetDeploymentParameterValues_NoEnvironmentParameterValuesAdded_ShouldReturnAllDefaultParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.GetDefaultParameterValue(deploymentParameters[0]);
            var parameterValue2 = definition.GetDefaultParameterValue(deploymentParameters[1]);
            var parameterValue3 = definition.GetDefaultParameterValue(deploymentParameters[2]);

            var parameterValues = definition.GetDeploymentParamaterValues(environment);

            Assert.AreEqual(deploymentParameters.Count(), parameterValues.Count());
            CollectionAssert.AllItemsAreInstancesOfType(parameterValues, typeof(IDefaultParameterValue));
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }

        [Test]
        public void GetDeploymentParameterValues_SomeEnvironmentParameterValuesNotAdded_ShouldReturnMixedEnvironmentAndDefaultParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.AddEnvironmentParameterValue(deploymentParameters[0], environment);
            var parameterValue2 = definition.GetDefaultParameterValue(deploymentParameters[1]);
            var parameterValue3 = definition.AddEnvironmentParameterValue(deploymentParameters[2], environment);

            var parameterValues = definition.GetDeploymentParamaterValues(environment);

            Assert.AreEqual(deploymentParameters.Count(), parameterValues.Count());
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }
        #endregion

        #region DeploymentTemplate
        [Test]
        public void AssignDeploymentTemplate_DeploymentTemplateNotAssigned_ShouldSetDefaultParameterValues()
        {
            definition = new DeploymentDefinition(Development.StubsBuilder.BuildVersionStub());
            var deploymentTemplate = StubsBuilder.BuildDeploymentTemplateStubWithAllParameterTypes();

            definition.DeploymentTemplate = deploymentTemplate;

            Assert.AreEqual(deploymentTemplate.Parameters.Count(), definition.ParameterValues.Count());
            foreach (var parameter in deploymentTemplate.Parameters)
                Assert.IsNotNull(definition.GetDefaultParameterValue(parameter));
        }

        [Test]
        public void AssignDeploymentTemplate_DeploymentTemplatePreviouslyAssigned_ShouldClearPreviousAssignedParameterValuesAndSetNewDefaultParameterValues()
        {
            definition = new DeploymentDefinition(Development.StubsBuilder.BuildVersionStub());
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var deploymentTemplate1 = StubsBuilder.BuildDeploymentTemplateStubWithAllParameterTypes();
            definition.DeploymentTemplate = deploymentTemplate1;
            definition.AddEnvironmentParameterValue(deploymentTemplate1.Parameters.First(), environment);
            var deploymentTemplate2 = StubsBuilder.BuildDeploymentTemplateStubWithAllParameterTypes();

            definition.DeploymentTemplate = deploymentTemplate2;

            Assert.AreEqual(deploymentTemplate2.Parameters.Count(), definition.ParameterValues.Count());
            foreach (var parameter in deploymentTemplate1.Parameters)
                Assert.IsNull(definition.GetDefaultParameterValue(parameter));
            foreach (var parameter in deploymentTemplate2.Parameters)
                Assert.IsNotNull(definition.GetDefaultParameterValue(parameter));
        }

        [Test]
        public void UnassignDeploymentTemplate_DeploymentTemplateAssigned_ShouldClearDefaultParameterValues()
        {
            definition = new DeploymentDefinition(Development.StubsBuilder.BuildVersionStub());
            var deploymentTemplate = StubsBuilder.BuildDeploymentTemplateStubWithAllParameterTypes();
            definition.DeploymentTemplate = deploymentTemplate;

            definition.DeploymentTemplate = null;

            CollectionAssert.IsEmpty(definition.ParameterValues.ToArray());
        }
        #endregion
    }
}
